﻿//  Copyright © Decebal Mihailescu 2013-2015
//  All rights reserved. This source code and binaries are distributed under
//  Microsoft Public PatternLicense (Ms-PL)
//  The latest code is available at http://VSDesignPatterns.codeplex.com

//  Redistributions of source code must retain the above copyright notice,
//  this list of conditions and the following disclaimer.
//  Redistributions in binary form must reproduce the above copyright notice,
//  this list of conditions and the following disclaimer in the documentation
//  and/or other materials provided with the distribution.

//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE. IT CAN BE DISTRIBUTED FREE OF CHARGE AS LONG AS THIS HEADER
//  REMAINS UNCHANGED.
using System;
//using System.ComponentModel.Design;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Runtime.InteropServices;
using EnvDTE80;
using Microsoft.VisualStudio;
using Microsoft.VisualStudio.CommandBars;
using Microsoft.Win32;
//using Microsoft.VisualStudio.OLE.Interop;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
//using Microsoft.Win32;
using System.Windows.Forms;
using EnvDTE;
using System.Reflection;
using System.IO;
using System.Collections.Generic;
using System.Linq;

namespace DesignPatterns
{
    using Common;
    using DesignPattterns;
    using DesignPatternsWizard;
    /// <summary>
    /// This is the class that implements the package exposed by this assembly.
    /// </summary>
    /// <remarks>
    /// <para>
    /// The minimum requirement for a class to be considered a valid package for Visual Studio
    /// is to implement the IVsPackage interface and register itself with the shell.
    /// This package uses the helper classes defined inside the Managed Package Framework (MPF)
    /// to do it: it derives from the Package class that provides the implementation of the
    /// IVsPackage interface and uses the registration attributes defined in the framework to
    /// register itself and its components with the shell. These attributes tell the pkgdef creation
    /// utility what data to put into .pkgdef file.
    /// </para>
    /// <para>
    /// To get loaded into VS, the package must be referred by &lt;Asset Type="Microsoft.VisualStudio.VsPackage" ...&gt; in .vsixmanifest file.
    /// </para>
    /// </remarks>
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [InstalledProductRegistration("#110", "#112", "1.0", IconResourceID = 400)] // Info on this package for Help/About
    [Guid(DesignPatternsPackage.PackageGuidString)]
    [SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "pkgdef, VS and vsixmanifest are valid VS terms")]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    //for loading package see http://blogs.msdn.com/b/allend/archive/2006/05/04/590055.aspx
    [ProvideAutoLoad(Microsoft.VisualStudio.Shell.Interop.UIContextGuids80.SolutionExistsAndNotBuildingAndNotDebugging)]// [ProvideAutoLoad(“ADFC4E64-0397-11D1-9F4E-00A0C911004F”)] 
    public sealed class DesignPatternsPackage : Package, IWin32Window, IPackageInstall//, IDTCommandTarget
    {
        /// <summary>
        /// DesignPatternsPackage GUID string.
        /// </summary>
        public const string PackageGuidString = "7e1225f1-fbdf-412d-8e3b-0af1d40eaaa3";
        readonly string _addInName = "DesignPatterns";
        IWizardWindow _wizWindow;
        private EnvDTE.SolutionEvents _solutionEvents;
        EnvDTE.SelectionEvents _selectionEvents;
        /// <summary>
        /// Initializes a new instance of the <see cref="DesignPatternsPackage"/> class.
        /// </summary>
        public DesignPatternsPackage()
        {
            // Inside this method you can place any initialization code that does not require
            // any Visual Studio service because at this point the package object is created but
            // not sited yet inside Visual Studio environment. The place to do all the other
            // initialization is the Initialize method.
            Debug.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString()));
            string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments),
                _addInName + "Run_" + Utils.VSVersion + ".log");

            Logging.Instance.Init(3, 1024 * 1024, path);

            Logging.Instance.Log(3, "VSPackage version {0} loaded at {1}, Win ver = {2}, Crt dir: '{3}', CLR version ={4}, Is64BitProcess = {5}, Process ID = {6}",
                Assembly.GetExecutingAssembly().GetName().Version.ToString(), DateTime.Now.ToString(CultureInfo.InvariantCulture), Environment.OSVersion.Version, Environment.CurrentDirectory, RuntimeEnvironment.GetSystemVersion(),
                sizeof(System.Int64) == 8, System.Diagnostics.Process.GetCurrentProcess().Id);
        }
        #region IServiceProvider Members
        protected override object GetService(Type serviceType)
        {
            if (serviceType == typeof(DTE2))
                return this.Dte;
            if (serviceType == typeof(IWin32Window) || serviceType == typeof(IPackageInstall) || serviceType == typeof(Package) || serviceType == typeof(IPackageInstall))
                return this;
            if (serviceType == typeof(IWizardWindow))
                return this._wizWindow;
            return base.GetService(serviceType);
        }
        #endregion

        #region Package Members
        /// <summary>
        /// Initialization of the package; this method is called right after the package is sited, so this is the place
        /// where you can put all the initialization code that rely on services provided by VisualStudio.
        /// </summary>
        protected override void Initialize()
        {//http://stackoverflow.com/questions/2642524/using-vsx-how-do-you-create-a-sub-menu-with-commands?rq=1
            base.Initialize();
            //    InitializeDTE();//dte.get should take care of it and replace it
            AddDesignPatterns.Initialize(this, LocationsEnum.ContextProject);
            AddDesignPatternsTools.Initialize(this, LocationsEnum.Tools);
            AddDesignPatternsNode.Initialize(this, LocationsEnum.Item);
            AddDesignPatternsProject.Initialize(this, LocationsEnum.MenuProject);
            AddDesignPatternsFolder.Initialize(this, LocationsEnum.Folder);
            AddDesignPatternsCode.Initialize(this, LocationsEnum.Code);
            try
            {
                PostInstallPackage((Handle == IntPtr.Zero) ? null : this);

            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "Initialization Error in VSDesignPackage:" + ex.Message);
                MessageBox.Show(ex.Message, "Exception in Package initialization");
                //Most of the error will be due to duplicate menu
                //We can ignore that errors
            }
        }


        public void PostInstallPackage(IWin32Window iwnd)
        {
            string crtFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            //for including link to TemplateLibrary.zip see http://stackoverflow.com/questions/25788656/including-a-batch-file-with-vsix
            string zipPath = Path.Combine(crtFolder, "TemplateLibrary.zip");
            try
            {

                if (!Directory.Exists(Path.Combine(crtFolder, "TemplateLibrary")) && File.Exists(zipPath))
                {
                    dynamic shellApplication = Activator.CreateInstance(Type.GetTypeFromProgID("Shell.Application"));
                    dynamic compressedFolderContents = shellApplication.NameSpace(zipPath).Items;
                    dynamic destinationFolder = shellApplication.NameSpace(crtFolder);
                    destinationFolder.CopyHere(compressedFolderContents);
                    Logging.Instance.Log(3, "Unpacked  TemplateLibrary in " + crtFolder);
                    if (!Directory.Exists(Path.Combine(crtFolder, "TemplateLibrary")))
                    {
                        if (iwnd == null)
                            MessageBox.Show("You have to allow for zip unpacking to use all patterns.", "Warning1");
                        else
                            iwnd.ShowMessageBox("You have to allow for zip unpacking to use all patterns.", "Warning1");
                    }

                }
            }
            catch (Exception ex)
            {

                Logging.Instance.Log(2, "Exception while unpacking the Package " + ex.Message);
                if (iwnd == null)
                    MessageBox.Show("Exception while unpacking the Package " + ex.Message, "Warning2");
                else
                    iwnd.ShowMessageBox("Exception while unpacking the Package " + ex.Message, "Warning2");
                //#if DEBUG
                //  MessageBox.Show(this, "Exception while unpacking the Package " + ex.Message + ex.GetType().ToString());//cross tread exception
                //#endif
            }
        }

        Dictionary<string, CommandBarButton> _commandBarButtons = new Dictionary<string, CommandBarButton>();
        private void CreateSearchMenus(DTE2 appObject)
        {
            // _CommandBarButtonEvents_ClickEventHandler handler = new _CommandBarButtonEvents_ClickEventHandler(DTEUtils.oControl_Click);
            //Available search menus
            //WindowPopupEnum all = WindowPopupEnum.CodeWindow | WindowPopupEnum.ErrorList | WindowPopupEnum.ReferenceItem | WindowPopupEnum.XAMLEditor;
            if (MEFController.Instance.SearchProviders == null)
                return;
            var menus = MEFController.Instance.SearchProviders.Select(p => new
            {
                SearchProviderName = p.Metadata.ProviderName,
                URL = p.Metadata.URL,
                //URL = p.GetNavigationUrl(null),
                SupportedWnds = p.SearchLocations,
                SP = p,
            });
            /*
            var menusByWndType_ = new[]
                {
                    new {name="Google", Url="http://www.google.com/search?q=" ,VSWndTypes = WindowPopupEnum.CodeWindow, Seq=1},
                    new {name="MSDN", Url="http://social.msdn.microsoft.com/search/en-US?query=" ,VSWndTypes = WindowPopupEnum.ErrorList, Seq=2},
                    new {name="Code Project", Url="http://www.codeproject.com/search.aspx?q=" ,VSWndTypes = WindowPopupEnum.ReferenceItem, Seq=3},
                    new {name="Stack Overflow", Url="http://stackoverflow.com/search?q=" ,VSWndTypes = all, Seq=4},
                    new {name="Yahoo", Url="http://search.yahoo.com/bin/search?p=" ,VSWndTypes = all, Seq=5},
                    new {name="Bing", Url="http://www.bing.com/search?q=" ,VSWndTypes = all, Seq=6},
                    new {name="Ask", Url="http://www.ask.com/web?q=" ,VSWndTypes = all, Seq=7}
                };*/
            WindowPopupEnum[] allLocations = Utils.GetAllEnumValues<WindowPopupEnum>();//ar.AsQueryable().Cast<ColumnsEnum>();
            //window names where the web-search add-in is available

#if DEBUG
            //a comprehensive list at http://stackoverflow.com/questions/2946211/visual-studio-commandbar-names
            CommandBars commandBars = (CommandBars)appObject.CommandBars;
            var sb = new System.Text.StringBuilder();

            foreach (CommandBar cbar in commandBars)
            {
                sb.AppendLine(cbar.Name);
            }
            string allCmds = sb.ToString();
            sb.Length = 0;
#endif
            var VSWindows = new[]
           { //new {VSWndType = "Code Window", Caption ="Search Selection Online"}, 
                new {VSWndType = WindowPopupEnum.CodeWindow, Caption ="Search Selection Online"},
               new {VSWndType = WindowPopupEnum.XAMLEditor, Caption ="Search Selection Online"},
               new {VSWndType = WindowPopupEnum.ReferenceItem, Caption ="Search Reference Online"},
               new {VSWndType = WindowPopupEnum.ErrorList, Caption ="Search Error Online"} ,
                new {VSWndType = WindowPopupEnum.HTML, Caption ="Search Selection Online"} ,
           };
            try
            {
                for (int j = 0; j < VSWindows.Length; j++)
                {
                    if (VSWindows[j].VSWndType == WindowPopupEnum.None)
                        continue;//no popUp for this VS window
                    CommandBar oCommandBar = ((CommandBars)appObject.CommandBars)[VSWindows[j].VSWndType.GetDescription()];
                    //Command command = oCommandBar.Parent as Command;
                    //oCommandBar.Position = MsoBarPosition.msoBarBottom;
                    //Add web-search menu to the specified oContext menus
                    /*
                //other good icons:548,523,472
                Command command = commands2.AddNamedCommand2(_addInInstance, cmdName,
                                  "Add " + cmdName + "...", "Executes the command for " + cmdName,
                                  true, 645, ref contextGUIDS,
                                  (int)vsCommandStatus.vsCommandStatusSupported |
                                  (int)vsCommandStatus.vsCommandStatusEnabled,
                                  (int)vsCommandStyle.vsCommandStylePictAndText,
                                  vsCommandControlType.vsCommandControlTypeButton);
                return command;
                     */
                    //Add the main menu
                    CommandBarPopup oPopup = (CommandBarPopup)
                    oCommandBar.Controls.Add(MsoControlType.msoControlPopup,
                    System.Reflection.Missing.Value,
                    System.Reflection.Missing.Value, 1, true);
                    oPopup.Caption = VSWindows[j].Caption;
                    // oPopup.CommandBar.Position = MsoBarPosition.msoBarBottom;

                    CommandBarButton self = oPopup.Control as CommandBarButton;
                    //Add a separator line
                    oPopup.BeginGroup = true;

                    //Add the search menus (sub-item)
                    int pos = 0;
                    //foreach (var item in menus)
                    foreach (SearchProvider item in MEFController.Instance.SearchProviders)
                    // for (int i = 0; i < menusByWndType.Length; i++)
                    {
                        SearchProvider searchProvider = item;//this assignment avoids lambda iteration bug in VS2008, otherwise only the last elemenent is yielded
                        // var item = menusByWndType[i];
                        if ((VSWindows[j].VSWndType & searchProvider.SearchLocations) == WindowPopupEnum.None)
                            continue;
                        pos++;
                        //Add the search menus (sub-item)
                        CommandBarButton oControl =
                            (CommandBarButton)oPopup.Controls.Add(MsoControlType.msoControlButton,
                            System.Reflection.Missing.Value,
                            System.Reflection.Missing.Value, pos, true);
                        oControl.Caption = searchProvider.Metadata.ProviderName;
                        oControl.Tag = searchProvider.Metadata.URL;
                        oControl.TooltipText = "Search " + searchProvider.Metadata.ProviderName + " for...";
                        //oControl.Click += handler;//deprecated
                        oControl.Click += new _CommandBarButtonEvents_ClickEventHandler((CommandBarButton ctrl, ref bool c) =>
                        {
                            //handler(ctrl, ref c);
                            DTE2 dte = (ctrl.Application as DTE2);
                            if (dte == null)
                                return;
                            object oContext = WindowPopupEnum.None;
                            SearchProvider.Context context = dte.ActiveWindow.GetSelectedContext(searchProvider);
                            if (context != null && !string.IsNullOrEmpty(context.SelectedText))
                            {
                                
                                try
                                {
                                    ItemOperations itemOperations = dte.ItemOperations;
                                    if (itemOperations != null)
                                    {
                                        string navigationUrl = searchProvider.GetNavigationUrl(context);
                                        switch (Utils.OnlineSearchDestination)
                                        {
                                            case 1:
                                                System.Diagnostics.Process proc = new System.Diagnostics.Process();
                                                proc.StartInfo = new ProcessStartInfo(navigationUrl);
                                                proc.Start();
                                                break;
                                            case 2:
                                                Window window = itemOperations.Navigate(navigationUrl, vsNavigateOptions.vsNavigateOptionsDefault);
                                                break;
                                            default:
                                                break;

                                        }
                                    }
                                }
                                catch
                                {


                                }
                            }
                        });

                        if (!_commandBarButtons.Any(kv => kv.Key == searchProvider.Metadata.ProviderName))
                            _commandBarButtons.Add(searchProvider.Metadata.ProviderName, oControl);//save subitem from garbage collection in VS2008
                    }
                    pos++;

                    //Add the search menus (sub-item)
                    CommandBarButton cControl =
                        (CommandBarButton)oPopup.Controls.Add(MsoControlType.msoControlButton,
                        System.Reflection.Missing.Value,
                        System.Reflection.Missing.Value, pos, true);
                    cControl.Caption = "Configure Online Search menus";
                    cControl.Tag = null;
                    cControl.TooltipText = "Search Configuration";
                    cControl.Click += new _CommandBarButtonEvents_ClickEventHandler((CommandBarButton ctrl, ref bool c) =>
                    {
                        using (ConfigForm cfg = new ConfigForm(3))
                        {
                            cfg.ShowDialog(this);
                        }

                    });
                    if (!_commandBarButtons.Any(kv => kv.Key == "SearchConfig " + VSWindows[j].VSWndType.GetDescription()))
                        _commandBarButtons.Add("SearchConfig " + VSWindows[j].VSWndType.GetDescription(), cControl);//save subitem from garbage collection in VS2008
                    oPopup.BeginGroup = false;
                }
              //  _bMenusInitialized = true;
            }
            catch (Exception ex)
            {

                Logging.Instance.Log(1, "Exception thrown in CreateContextMenu :{0}", ex.Message);
            }
        }
        private EnvDTE80.DTE2 _dte;
        private DteInitializer _dteInitializer;

        internal DTE2 Dte
        {
            get
            {//initialize dte properly
                if (_dte == null)
                {
                    IVsShell shellService;

                    this._dte = this.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE)) as EnvDTE80.DTE2;

                    if (this._dte == null) // The IDE is not yet fully initialized
                    {
                        shellService = this.GetService(typeof(SVsShell)) as IVsShell;
                        this._dteInitializer = new DteInitializer(shellService, this.InitializeDTE);
                        this._dte = this.GetAsService<Microsoft.VisualStudio.Shell.Interop.SDTE>() as EnvDTE80.DTE2;
                    }
                    else
                    {
                        this._dteInitializer = null;
                    }

                }
                return _dte;
            }

        }

        public IntPtr Handle
        {
            get
            {
                try
                {
                    if (Dte == null || Dte.MainWindow == null)
                        return IntPtr.Zero;
                    // Wraps the Win32 handle of the Visual Studio main window
                    return new System.IntPtr(Dte.MainWindow.HWnd);
                }
                catch
                {

                    return IntPtr.Zero;
                }
            }
        }

        private void InitializeDTE()
        {
            IVsShell shellService;

            this._dte = this.GetService(typeof(Microsoft.VisualStudio.Shell.Interop.SDTE)) as EnvDTE80.DTE2;

            if (this._dte == null) // The IDE is not yet fully initialized
            {
                shellService = this.GetService(typeof(SVsShell)) as IVsShell;
                this._dteInitializer = new DteInitializer(shellService, this.InitializeDTE);
            }
            else
            {
                this._dteInitializer = null;
            }
        }
        bool _bMenusInitialized = false;
        internal void SetMenuAppearance(OleMenuCommand oMenuCommand, LocationsEnum location)
        {
            LocationsEnum loc = WizardUtils.Locations;
            oMenuCommand.Visible = oMenuCommand != null && (loc & location) != LocationsEnum.None;
            if (oMenuCommand.Visible)
            {
                oMenuCommand.Visible = VsShellUtilities.ShellIsInitialized && !VsShellUtilities.IsSolutionBuilding(this);
                if (oMenuCommand.Visible)
                {
                    var solution = Dte.Solution;
                    if (solution != null && solution.IsOpen)
                    {
                        Project prj = Dte.GetSelectedProject();
                        if (_wizWindow == null && prj != null && prj.GuessProjectType() != CodeLangEnum.None)
                        {
                            oMenuCommand.Enabled = true;
                          //  oMenuCommand.Text = "Add design Patterns to " + prj.Name;

                        }
                        else
                            oMenuCommand.Enabled = false;
                    }
                    else
                    {
                        oMenuCommand.Visible = false;
                    }
                }
            }
            if (!_bMenusInitialized && MEFController.Instance.SearchProviders != null)
                try
                {
                    if (Dte != null)
                    {
                        if (Utils.ShowSearchMenus)
                        {
                            _bMenusInitialized = true;
                            CreateSearchMenus(Dte);
                        }

                        if (Dte.Events != null && Dte.Events.SolutionEvents != null)
                        {
                            _solutionEvents = Dte.Events.SolutionEvents;
                            _solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(_solutionEvents_Opened);
                            _solutionEvents.BeforeClosing += new _dispSolutionEvents_BeforeClosingEventHandler(_solutionEvents_BeforeClosing);
                            _selectionEvents = Dte.Events.SelectionEvents;
                            _selectionEvents.OnChange += new _dispSelectionEvents_OnChangeEventHandler(_selectionEvents_OnChange);
                            //  _bMenusInitialized = true;
                        }
                        else
                            this.ShowMessageBox(Dte.Events == null ? "Dte.Events is null" : "Dte.Events.SolutionEvents is null", "Package initialization");

                    }
                    else
                        this.ShowMessageBox("Dte is null", "Package initialization");


                    // _errProvider = new Microsoft.VisualStudio.Shell.ErrorListProvider(this);
                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(1, "Initialization Error in VSDesignPackage while setting menus:" + ex.Message);
                    this.ShowMessageBox(ex.Message, "Exception in Package initialization");
                }

        }
        /*
        internal void MenuItem_BeforeQueryStatus(object sender, EventArgs e)
        {
            OleMenuCommand menuCommand = sender as OleMenuCommand;
            // I don't need this next line since this is a lambda.
            // But I just wanted to show that sender is the OleMenuCommand.

            // EnvDTE80.DTE2 service = (EnvDTE80.DTE2)package.GetService(typeof(EnvDTE80.DTE2));
            // EnvDTE80.DTE2 service = ServiceProvider.GetService(typeof(EnvDTE80.DTE2)) as EnvDTE80.DTE2;
            bool bShow = menuCommand != null && VsShellUtilities.ShellIsInitialized && !VsShellUtilities.IsSolutionBuilding(this);
            if (bShow)
            {
                var solution = Dte.Solution;
                if (solution != null)
                    menuCommand.Enabled = solution.IsOpen;
            }
        }
        */
        Project TargetProject
        {
            get
            {
                Project proj = Dte.GetSelectedProject();
                if (proj != null)
                    return proj;
                proj = Dte.GetActiveSolutionProject();
                if (proj != null)
                    return proj;
                //last resort attempt
                //proj = _applicationObject.GetActiveSolutionProject();
                //if (proj == null)
                //{
                //    int cnt = _applicationObject.Solution.Projects.Count;
                //    proj = _applicationObject.Solution.Projects.Item(cnt/2);
                //}
                //return proj;

                ProjectItem pi = null;
                try
                {
                    string name = Guid.NewGuid().ToString() + ".txt";
                    pi = Dte.ItemOperations.AddNewItem("General\\Text File", name);
                    if (pi != null)
                        proj = pi.ContainingProject;
                }
                finally
                {
                    if (pi != null && proj != null)
                    {
                        proj.DeleteProjectItem(pi.Name);
                    }
                }
                return proj;
            }
        }
        internal bool ShowWizardWindow(Project prj = null)
        {
            if (_wizWindow != null)
                return false;
            if (prj == null)
                _wizWindow = TargetProject.CreateWizardWindowFromProject(this);
            else
                _wizWindow = prj.CreateWizardWindowFromProject(this);
            if (_wizWindow == null)
            {
                MessageBox.Show(this,
                    string.Format("Unable to create the wizard for project{0} and the {1} language",
                    TargetProject.Name, TargetProject.GuessProjectType()));
                return false;
            }
            IProjectInfo prjinf = _wizWindow.GetAsService<IProjectInfo>();
            if (prjinf == null)
            {
                MessageBox.Show(this,
                    string.Format("The wizard for project {0} and the {1} language\r\n did not implemented correctly the IServiceProvider interface",
                    TargetProject.Name, TargetProject.GuessProjectType()));
                return false;

            }
            prjinf.UpdateVSProject += new UpdateVSProjectDelegate(wizForm_GeneratePattern);
            prjinf.CanUpdateVSProject += new CanUpdateVSProjectDelegate(wizForm_CanGeneratePattern);
            _wizWindow.WizardWindowClosed += new WizardWindowClosedDelegate(wizForm_WizardWindowClosed);
            _wizWindow.ShowWizardWindow(this);//ShowDialog is better but does not work with next and prev buttons
            return true;
        }
        //https://msdn.microsoft.com/en-us/library/cc826118.aspx?f=255&MSPPError=-2147217396 for IDG_VS_PROJ_ADD
        //http://stackoverflow.com/questions/2642524/using-vsx-how-do-you-create-a-sub-menu-with-commands
        internal void MenuItemCallback(object sender, EventArgs e)
        {

            /*
            if (sender is OleMenuCommand)
            {                OleMenuCommand olec = sender as OleMenuCommand;
                var cid = olec.CommandID;
                var g = cid.Guid;
                int id = cid.ID;
                string message = string.Format(CultureInfo.CurrentCulture, "olecmd id:{0},guid:{1}", id,g);
                string title = this.GetType().FullName;

                // Show a message box to prove we were here
                VsShellUtilities.ShowMessageBox(
                    this,
                    message,
                    title,
                    OLEMSGICON.OLEMSGICON_INFO,
                    OLEMSGBUTTON.OLEMSGBUTTON_OK,
                    OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);

            }
            */
            OleMenuCommand oMenuCommand = sender as OleMenuCommand;
            if (oMenuCommand != null)
                oMenuCommand.Enabled = false;

            bool bhandled = ShowWizardWindow();

            Logging.Instance.Log(3, "In Exec: ShowWizardWindow returned :{0}", bhandled);
        }
        void wizForm_CanGeneratePattern(IServiceProvider isp, EventValidaton evval)
        {
            evval.ErrorMsg = null;
            evval.Result = ResultEnum.UnknownError;
            switch (evval.SelIndex)
            {
                case 2:

                    if (isp == null)
                    {
                        evval.ErrorMsg = "The Sender is null or not a IServiceProvider";
                        Logging.Instance.Log(2, "In Connect.wizForm_CanGeneratePattern " + evval.ErrorMsg);
                        return;
                    }
                    IProjectInfo inf = isp.GetAsService<IProjectInfo>();
                    // Project proj = Dte.GetProjectbyInfo(inf);
                    Project proj = inf.GetAsService<Project>();
                    if (proj == null)
                    {
                        evval.ErrorMsg = "Can not get the project reference for " + ((inf == null) ? "null IprojectInfo" : inf.FileName);
                        Logging.Instance.Log(2, "In Connect.wizForm_CanGeneratePattern " + evval.ErrorMsg);
                        return;
                    }
                    ILanguageProvider lp = (evval.LangProvider == null) ? inf.GetAsService<ILanguageProvider>() : evval.LangProvider;
                    if (evval.Files == null)
                    {
                        evval.Result = ResultEnum.Success;
                        Logging.Instance.Log(2, "In Connect.wizForm_CanGeneratePattern No Files found");
                        return;
                    }
                    foreach (string newfile in evval.Files)
                    {
                        evval.Result = CanAddToProject(proj, newfile, lp, out evval.ErrorMsg);
                        if (evval.Result != ResultEnum.Success)
                        {
                            Logging.Instance.Log(2, "In Connect.wizForm_CanGeneratePattern failed to add file" + newfile);
                            break;
                        }

                    }

                    break;
                default:
                    evval.Result = ResultEnum.Success;
                    break;
            }
        }

        private static ResultEnum CanAddToProject(Project proj, string newfile, ILanguageProvider lp, out string ErrorMsg)
        {
            ResultEnum result = ResultEnum.UnknownError;
            ErrorMsg = null;
            try
            {
                if (proj == null)
                {
                    ErrorMsg = "The Project reference is null";
                    Logging.Instance.Log(1, ErrorMsg + " in Connect.CanAddToProject");
                    return ResultEnum.AutomationError;
                }
                if (lp == null)
                {
                    ErrorMsg = "The Language Provider is null";
                    Logging.Instance.Log(1, ErrorMsg + " in Connect.CanAddToProject");
                    return ResultEnum.UnknownError;
                }
                newfile = Path.GetFileName(newfile);
                //check for physical files in the project folder
                bool bFileExists = proj.HasFile(newfile);

                if (bFileExists)
                {
                    ErrorMsg = string.Format("The file '{0}' already exists in '{1}' project's folder", newfile, proj.Name);
                    Logging.Instance.Log(1, "In Connect.CanAddToProject" + ErrorMsg);
                    return ResultEnum.FileExists;
                }
                //check for incompatible file types

                string ext = Path.GetExtension(newfile);

                if (!lp.IsValidExtension(ext))
                {
                    ErrorMsg = string.Format("The  '{0}' extension is incompatible with project's Language provider'{1}'", ext, lp.ToString());
                    Logging.Instance.Log(1, "In Connect.CanAddToProject" + ErrorMsg);
                    return ResultEnum.UnknownError;
                }


                Logging.Instance.Log(3, "In Connect.CanAddToProject proj.Kind :{0}, proj.Name = {1}, lang = {2}", proj.Kind, proj.Name, lp.ToString());

                //check for items named the same
                ProjectItem foundItem = proj.FindProjectItemByName(newfile);
                if (foundItem != null)//preexisting element
                {
                    if (!foundItem.get_IsOpen(EnvDTE.Constants.vsViewKindCode))
                    {
                        Window w = foundItem.Open(EnvDTE.Constants.vsViewKindCode);
                        w.Visible = true;
                        w.Activate();
                    }
                    else
                    {
                        foundItem.Document.Activate();
                    }
                    ErrorMsg = string.Format("FileItem '{0}' already exists in the '{1}' project", newfile, proj.Name);
                    Logging.Instance.Log(1, "In Connect.CanAddToProject" + ErrorMsg);
                    return ResultEnum.ItemExists;
                }
                else //new element
                    result = ResultEnum.Success;
            }
            catch (Exception ex)
            {
                Logging.Instance.Log(1, "In Connect.CanAddToProject exception thrown:" + ex.Message);
                ErrorMsg = ex.Message;
            }
            return result;
        }


        void wizForm_WizardWindowClosed(object sender, EventWizardWindowClosed evclosed)
        {
            if (_wizWindow != null)
            {
                IProjectInfo prjinf = _wizWindow.GetAsService<IProjectInfo>();
                if (prjinf != null)
                {
                    prjinf.UpdateVSProject -= new UpdateVSProjectDelegate(wizForm_GeneratePattern);
                    prjinf.CanUpdateVSProject -= new CanUpdateVSProjectDelegate(wizForm_CanGeneratePattern);

                    if (_wizWindow.IsRestarting)//to debug use !
                    {
                        _wizWindow.Dispose();
                        _wizWindow = null;
                        string file = Dte.Application.Solution.FullName;
                        //_addInInstance.DTE.Application.Quit();// will crash Visual studio
                        Dte.Quit();
                        //set stating Point

                        RegistryKey reg = Registry.CurrentUser.CreateSubKey(Common.Utils.AppHive, RegistryKeyPermissionCheck.Default);
                        if (reg != null)
                        {
                            reg.SetValue("SolutionName", (file == null || !File.Exists(file)) ? "nothing" : file, RegistryValueKind.String);
                            reg.Close();
                        }


                        // Launch itself as with elevated role
                        ProcessStartInfo proc = new ProcessStartInfo();
                        proc.UseShellExecute = true;
                        proc.WorkingDirectory = Environment.CurrentDirectory;
                        proc.FileName = Utils.VSPath;
                        //warning  /resetaddin  DesignPatterns.Connect might result in wizard window lock up
                        string addInName = this.GetType().FullName;
                        proc.Arguments = "\"" + file + "\"" + " /resetaddin  " + addInName;
                        proc.Verb = "runas";
                        System.Diagnostics.Process.Start(proc);


                        return;
                    }

                }
                _wizWindow.Dispose();
            }
            _wizWindow = null;
        }

        void wizForm_GeneratePattern(IServiceProvider isp, EventGeneration evgen)
        {


            evgen.ErrorMsg = null;
            evgen.Result = ResultEnum.UnknownError;

            if (isp == null)
            {
                evgen.ErrorMsg = "The Sender is null or not a IServiceProvider";
                return;
            }
            IProjectInfo inf = isp.GetAsService<IProjectInfo>();
            // Project proj = Dte.GetProjectbyInfo(inf);
            Project proj = inf.GetAsService<Project>();
            if (proj == null)
            {
                evgen.ErrorMsg = "Can not get the project reference for " + ((inf == null) ? "null IprojectInfo" : inf.FileName);
                return;
            }
            ILanguageProvider lp = isp.GetAsService<ILanguageProvider>();
            if (lp == null)
            {
                evgen.ErrorMsg = "Can not get the the language Provider reference for " + proj.Name;
                return;
            }
            bool stop = false;
            if (evgen.Files == null)
            {
                evgen.ErrorMsg = "No files found";
                return;
            }

            foreach (string itemName in evgen.Files.Select(x => x.itemName))
            {
                evgen.Result = CanAddToProject(proj, itemName, lp, out evgen.ErrorMsg);
                if (evgen.Result != ResultEnum.Success)
                {
                    stop = true;
                    Logging.Instance.Log(3, "Can not add '{0}' to project because {1}", itemName, evgen.ErrorMsg);
                    if (MessageBox.Show(this, evgen.ErrorMsg + "\r\nYou can atempt to resolve the conflicts before generating again.\r\nDo you want to retry?",
                            "DesignPatterns conflict", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question) == DialogResult.Retry)
                        evgen.Result = ResultEnum.UserCancelled;
                    else
                    {
                        evgen.Result = ResultEnum.Success;
                    }
                    break;
                }

            }
            if (evgen.Result == ResultEnum.Success)
            {
                switch (Utils.OnlineResourcesDestination)
                {
                    case 1:
                        System.Diagnostics.Process proc = new System.Diagnostics.Process();
                        proc.StartInfo = new ProcessStartInfo(evgen.Url);
                        proc.Start();
                        break;
                    case 2:
                        ItemOperations itemOperations = Dte.ItemOperations;
                        if (itemOperations != null)
                        {
                            Window window = itemOperations.Navigate(evgen.Url, vsNavigateOptions.vsNavigateOptionsDefault);
                        }
                        break;
                    default:
                        break;

                }

            }
            if (stop)
                return;

            bool rollback = false;
            List<string> rollbackList = new List<string>();
            foreach (FileData fd in evgen.Files)
            {

                ProjectItem pi = null;
                try
                {
                    if (inf.CodeLang == CodeLangEnum.ManagedCpp || inf.CodeLang == CodeLangEnum.NativeCpp)
                    {

                        string path = Path.Combine(proj.GetProjectProperties()["ProjectDirectory"], fd.itemName);
                        File.Move(fd.fileNameCopy, path);
                        pi = proj.ProjectItems.AddFromFile(path);
                    }
                    else
                        pi = proj.ProjectItems.AddFromFileCopy(fd.fileNameCopy);
                    if (pi == null)
                    {
                        Logging.Instance.Log(3, "Unable to add ProjectItem");
                        if (MessageBox.Show(this, "Unable to add ProjectItem" + "\r\nYou can atempt to resolve the conflicts before generating again.\r\nDo you want to retry?",
                            "DesignPatterns generation error", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question) == DialogResult.Retry)
                            evgen.Result = ResultEnum.AutomationError;
                        else
                            evgen.Result = ResultEnum.Success;
                        evgen.ErrorMsg = "Unable to add ProjectItem";
                        rollback = true;
                        evgen.ErrorMsg = "Can not AddNewItem to _applicationObject.ItemOperations";
                        return;
                    }

                    // VC++ project will not change SearchProviderName here and crash later
                    if (inf.CodeLang != CodeLangEnum.ManagedCpp && inf.CodeLang != CodeLangEnum.NativeCpp)
                    {
                        pi.Name = fd.itemName;
                    }

                    if (File.Exists(fd.fileNameCopy))
                        File.Delete(fd.fileNameCopy);
                    evgen.Result = ResultEnum.Success;

                }
                catch (Exception ex)
                {

                    Logging.Instance.Log(3, "pi.Name thrown exception :{0}", ex.Message);
                    if (MessageBox.Show(this, ex.Message + "\r\nYou can atempt to resolve the conflicts before generating again.\r\nDo you want to retry?",
                        "DesignPatterns conflict", MessageBoxButtons.RetryCancel, MessageBoxIcon.Question) == DialogResult.Retry)
                        evgen.Result = ResultEnum.AutomationError;
                    else
                        evgen.Result = ResultEnum.Success;
                    evgen.ErrorMsg = ex.Message;
                    rollback = true;
                    if (File.Exists(fd.fileNameCopy))
                        File.Delete(fd.fileNameCopy);
                    break;

                }
                finally
                {
                    if (evgen.Result != ResultEnum.Success)
                    {
                        if (pi != null)
                        {
                            pi.ContainingProject.DeleteProjectItem(pi.Name);

                            pi = null;

                        }
                        else
                            rollbackList.Add(fd.itemName);
                    }
                    else
                        rollbackList.Add(pi.Name);

                }

                if (evgen.Result != ResultEnum.Success)
                {
                    if (File.Exists(fd.fileNameCopy))
                        File.Delete(fd.fileNameCopy);
                    break;
                }
            }
            //roll back previous changes

            if (rollback)
            {
                rollbackList.ForEach(n => proj.DeleteProjectItem(n));
            }
            else
            {

                rollbackList.ForEach(itemName =>
                {
                    try
                    {
                        ProjectItem pi = proj.ProjectItems.FindProjectItemByName(itemName);
                        if (!pi.get_IsOpen(EnvDTE.Constants.vsViewKindCode))
                        {
                            Window w = pi.Open(EnvDTE.Constants.vsViewKindCode);
                            w.Visible = true;
                            w.Activate();
                        }
                        else
                        {
                            pi.Document.Activate();
                        }
                    }
                    catch (Exception ex)
                    {

                        Logging.Instance.Log(3, "Exception thrown while showing '{0}':{1}", itemName, ex.Message);
                    }
                });
            }
        }
        /*  //never called
          public void QueryStatus(string CmdName, vsCommandStatusTextWanted NeededText, ref vsCommandStatus StatusOption, ref object CommandText)
          {
              //   throw new NotImplementedException();
          }
          //never called
          public void Exec(string CmdName, vsCommandExecOption ExecuteOption, ref object VariantIn, ref object VariantOut, ref bool Handled)
          {
              //  throw new NotImplementedException();
          }*/
        #endregion

        bool _solClosing = true;
        void _solutionEvents_BeforeClosing()
        {
            _solClosing = true;
            Logging.Instance.Log(3, "In _solutionEvents_BeforeClosing: entererd _solClosing :{0}", _solClosing);
        }

        void _selectionEvents_OnChange()
        {
            Logging.Instance.Log(3, "In _selectionEvents_OnChange: entererd _solClosing :{0}", _solClosing);

            if (_solClosing || !WizardUtils.HasEscalatedPrivileges(System.Security.Principal.WindowsBuiltInRole.Administrator))
                return;


            //add project selection and show wizard
            //get stating Point
            try
            {
                Project proj = Dte.GetActiveSolutionProject();
                if (proj == null)
                    return;
                using (RegistryKey reg = Registry.CurrentUser.OpenSubKey(Common.Utils.AppHive, RegistryKeyPermissionCheck.Default))
                {
                    if (reg != null)
                    {
                        string file1 = reg.GetValue("SolutionName", null, RegistryValueOptions.None) as string;
                        if (string.Compare(Dte.Application.Solution.FullName, file1, true) == 0)
                        {
                            //add project selection and show wizard
                            bool bhandled = ShowWizardWindow();
                            if (bhandled)
                            {
                                //    reg.DeleteValue("SolutionName");
                                Logging.Instance.Log(3, "In _selectionEvents_OnChange: ShowWizardWindow returned :{0}", bhandled);
                            }
                        }
                    }
                }
            }
            catch
            {

            }


        }
        private void _solutionEvents_Opened()
        {
            _solClosing = false;

            Logging.Instance.Log(3, "In _solutionEvents_Opened: entererd _solClosing :{0}", _solClosing);

            /*
                        int cnt = _applicationObject.Solution.Projects.Count;
                        Project prj =_applicationObject.Solution.Projects.Item(cnt / 2);
                        string SearchProviderName  = prj.FullName;
             * */
            /*
            //add project selection and show wizard
            if ( !WizardUtils.HasEscalatedPrivileges(System.Security.Principal.WindowsBuiltInRole.Administrator))
                return;
            //get stating Point
           
                        try
                        {
                            using (RegistryKey reg = Registry.CurrentUser.OpenSubKey(Common.Utils.AppHive, RegistryKeyPermissionCheck.Default))
                            {
                                if (reg != null)
                                {
                                    string file1 = reg.GetValue("SolutionName", null, RegistryValueOptions.None) as string;
                                    if (string.Compare(_addInInstance.DTE.Application.Solution.FullName, file1, true) == 0)
                                    {
                                        //must select a project 
                                        //can't get the selected project, use _selectionEvents_OnChange instead
                                        bool handled = ShowWizardWindow();
                                        //add project selection and show wizard
                                     
                                        if (handled)
                                            reg.DeleteValue("SolutionName");
                                    }
                                }
                            }
                        }
                        catch
                        {

                        }

                */
            // MessageBox.Show(this,"Solution opened");

        }
    }
    //see http://www.mztools.com/articles/2013/MZ2013029.aspx
    internal class DteInitializer : IVsShellPropertyEvents
    {
        private IVsShell shellService;
        private uint cookie;
        private Action callback;

        internal DteInitializer(IVsShell shellService, Action callback)
        {
            int hr;

            this.shellService = shellService;
            this.callback = callback;

            // Set an event handler to detect when the IDE is fully initialized
            hr = this.shellService.AdviseShellPropertyChanges(this, out this.cookie);

            Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr);
        }

        int IVsShellPropertyEvents.OnShellPropertyChange(int propid, object var)
        {
            int hr;
            bool isZombie;

            if (propid == (int)__VSSPROPID.VSSPROPID_Zombie)
            {
                isZombie = (bool)var;

                if (!isZombie)
                {
                    // Release the event handler to detect when the IDE is fully initialized
                    hr = this.shellService.UnadviseShellPropertyChanges(this.cookie);

                    Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr);

                    this.cookie = 0;

                    this.callback();
                }
            }
            return VSConstants.S_OK;
        }
    }
}
